﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading.Tasks;
using SLControlLibrary;
using Hydra.Shell.Attributes;

namespace Hydra.Shell.Interfaces
{
    public class ServiceExceptionEventArgs : EventArgs
    {
        public Exception Exception { get; set; }

        public ServiceExceptionEventArgs()
        {
        }

        public ServiceExceptionEventArgs(Exception ex)
        {
            this.Exception = ex;
        }
    }

    public class ServiceLoadEventArgs : EventArgs
    {
        public IUnknown Service { get; set; }

        public ServiceLoadEventArgs()
        {
        }

        public ServiceLoadEventArgs(IUnknown service)
        {
            this.Service = service;
        }
    }

    public delegate void ServiceLoadHandler(object sender, ServiceLoadEventArgs e);
    public delegate void ServiceExceptionHandler(object sender, ServiceExceptionEventArgs e);

    public class ServiceLoadOperation
    {
        public event ServiceLoadHandler ServiceLoaded;
        public event ServiceExceptionHandler ExceptionOccured;
        public IUnknown Service { get; set; }
        public bool ServiceAvailable { get; set; }
        public Exception Exception { get; set; }
        protected UIThread uiThread;

        public ServiceLoadOperation(UIThread uiThread)
        {
            this.uiThread = uiThread;
        }

        public ServiceLoadOperation(Exception ex, UIThread uiThread)
        {
            this.Exception = ex;
            this.uiThread = uiThread;
        }

        public bool HasException
        {
            get
            {
                return this.Exception != null;
            }
        }

        public void SetLoaded(IUnknown service)
        {
            this.Service = service;
            this.ServiceAvailable = true;

            ServiceLoaded(this, new ServiceLoadEventArgs(service));
        }

        public void SetException(Exception ex)
        {
            ExceptionOccured(this, new ServiceExceptionEventArgs(ex));
        }

        public void DelaySetLoaded(IUnknown service)
        {
            this.Service = service;
            this.ServiceAvailable = true;

            uiThread.Run(() =>
            {
                ServiceLoaded(this, new ServiceLoadEventArgs(service));
            });
        }

        public void DelaySetException(Exception ex)
        {
            this.Exception = ex;

            uiThread.Run(() =>
            {
                ExceptionOccured(this, new ServiceExceptionEventArgs(ex));
            });
        }

        protected void RaiseServiceLoaded()
        {
            ServiceLoaded(this, new ServiceLoadEventArgs(this.Service));
        }
    }

    public class ServiceLoadOperation<T> : ServiceLoadOperation where T : IUnknown
    {
        public new T Service
        {
            get
            {
                return (T) base.Service;
            }

            set
            {
                base.Service = value;
            }
        }

        public ServiceLoadOperation(UIThread uiThread) : base(uiThread)
        {
        }

        public void DelaySetLoaded(T service)
        {
            this.Service = service;
            this.ServiceAvailable = true;

            uiThread.Run(() =>
            {
                RaiseServiceLoaded();
            });
        }

        public void SetLoaded(T service)
        {
            this.Service = service;
            this.ServiceAvailable = true;

            RaiseServiceLoaded();
        }
    }

    [Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
    public interface IQueryServiceProvider : IUnknown
    {
        bool QueryService<T>(Guid guidService, out ServiceLoadOperation<T> serviceLoadOperation) where T : IUnknown;
        bool QueryService(Guid guidService, Guid guidInterFace, out ServiceLoadOperation serviceLoadOperation);
    }
}
